package noodws

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
	"net/http"
	"nood/noodframe/nooderrcode"
	"nood/noodframe/noodfun"
	"nood/noodframe/noodlog"
	"runtime/debug"
	"time"
)

func WebSocketRun(service *gin.Engine) {
	service.GET("/ws", WebSocketHandler)
}

func WebSocketHandler(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			debugStackS := string(debug.Stack())
			fmt.Println("Runtime panic WebSocketHandler：", debugStackS)
		}
	}()
	websocket := websocket.Upgrader{
		ReadBufferSize:   1024,
		WriteBufferSize:  1024,
		HandshakeTimeout: 5 * time.Second,
		// 取消ws跨域校验
		CheckOrigin: func(r *http.Request) bool {
			return true
		}}
	// 获取WebSocket连接
	ws, err := websocket.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		panic(err)
	}
	//创建上下文
	ctx := newCTX(ws)
	// 处理WebSocket消息
	for {
		_, wsMsg, err := ws.ReadMessage()
		if err != nil {
			break
		}
		ctx.setRequest(wsMsg)
		ctx.handleRequest()
	}
	// 关闭WebSocket连接
	ctx.closeWs()
}

func newCTX(ws *websocket.Conn) *WsContext {
	ctx := &WsContext{
		Context: context.Background(),
		Client:  ws,
	}
	return ctx
}

func (ctx *WsContext) Bind(data interface{}) error {
	temp, err := json.Marshal(ctx.Request.Data)
	if err != nil {
		return err
	}
	err = json.Unmarshal(temp, data)
	return err
}

// 返回响应信息
func (ctx *WsContext) SetResponse(data interface{}) {
	response := setResponse(ctx.Request.Cmd, ctx.Request.Seq, data)
	ctx.send(response)
}

// 返回错误信息
func (ctx *WsContext) SetError(code int32, Msg string) {
	response := setError(ctx.Request.Cmd, ctx.Request.Seq, code, Msg)
	ctx.send(response)
}

// 返回带有参数的错误信息
func (ctx *WsContext) SetErrorAndData(code int32, Msg string, data interface{}) {
	response := setErrorAndData(ctx.Request.Cmd, ctx.Request.Seq, code, Msg, data)
	ctx.send(response)
}

func (ctx *WsContext) SetErrorCode(code int32) {
	response := setErrorCode(ctx.Request.Cmd, ctx.Request.Seq, code)
	ctx.send(response)
}

func (ctx *WsContext) setRequest(wsMsg []byte) *WsContext {
	req := &Request{}
	err := json.Unmarshal(wsMsg, req)
	if err != nil {
		noodlog.Error("setRequest err", zap.Any("err", err))
	}
	ctx.Request = req
	return ctx
}

func (ctx *WsContext) handleRequest() {
	defer noodfun.RecoverDebugStackS()
	//不在白名单的路由都要验证登录信息，未登录的用户返回错误
	if _, ok := getWhiteList(ctx.Request.Cmd); !ok {
		if ctx.UID == 0 {
			ctx.SetErrorCode(nooderrcode.LoginErr)
			return
		}
	}
	//对于未注册的路由直接返回错误
	if fun, ok := getWsHandlerMap(ctx.Request.Cmd); ok {
		if _, ok := getWhiteList(ctx.Request.Cmd); !ok {
			cloneCTX := *ctx //克隆一份请求参数
			noodfun.TryGo(func() {
				fun(&cloneCTX)
			})
		} else { //需要保存上下文信息，不进行克隆
			fun(ctx)
		}
	} else {
		ctx.SetErrorCode(nooderrcode.CMDErr)
	}
}

func (ctx *WsContext) send(response interface{}) {
	sendData := &sendDataChanType{
		WsC:  ctx,
		Data: response,
	}
	sendDataChan <- sendData
}

func (ctx *WsContext) closeWs() {
	DelUserClientMap(ctx.UID) //删除下线的用户
	ctx.Client.Close()
}

func setResponse(cmd string, seq string, data interface{}) *Response {
	response := &Response{
		Cmd:  cmd,
		Seq:  seq,
		Code: 0,
		Msg:  "",
		Data: data,
	}
	return response
}

func setError(cmd string, seq string, code int32, Msg string) *Response {
	response := &Response{
		Cmd:  cmd,
		Seq:  seq,
		Code: code,
		Msg:  Msg,
		Data: struct{}{},
	}
	return response
}

// 返回带有参数的错误信息
func setErrorAndData(cmd string, seq string, code int32, Msg string, data interface{}) *Response {
	response := &Response{
		Cmd:  cmd,
		Seq:  seq,
		Code: code,
		Msg:  Msg,
		Data: data,
	}
	return response
}

func setErrorCode(cmd string, seq string, code int32) *Response {
	response := &Response{
		Cmd:  cmd,
		Seq:  seq,
		Code: code,
		Msg:  nooderrcode.GetErrorMsg(code),
		Data: struct{}{},
	}
	return response
}
